package com.jse;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import com.jse.tpl.Jst;
import com.jse.tpl.Tpl;

public class Fs {
	
	public static Charset CHARSET=StandardCharsets.UTF_8;
	public final static String LINE=File.separatorChar=='\\'?"\r\n":"\n";

	public static String read(String file) {return readString(Path.of(file));}
	public static String read(String parent, String file) {return readString(parent, file);}
	public static String readString(String path) {return readString("",path);}
	public static String readString(String parent, String file){
		try {
			Path p=null;
			if(parent.equals("classpath"))p=Path.of(Jse.classpath,file);
			else if(parent.equals("js")||parent.equals("jspath"))p=Path.of(Jse.jspath(),file);
			else if(parent.equals("webapp")||parent.equals("web"))p=Path.of(Jse.webapp(),file);
			else if(parent.equals("webapps"))p=Path.of(Jse.webapps(),file);
			else if(parent.equals("jse")||parent.equals("jar")) {//resource一般不会太大 太大用Io.read 一搬不大直接读
				return new String(Fs.class.getResourceAsStream(file).readAllBytes(),CHARSET);
			}else p=Path.of(parent,file);
			return Files.readString(p,CHARSET);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 自定义模板与指令 __jse__include __jse__xxx 变量后跟|则表示变量结束,如遇到;或者行尾则可省略
	 * @param parent 路径前缀字符串
	 * @param file 文件路径字符串
	 * @param map {} 数据集,如需要缓存请指定 cache.file=1_000 表示缓存时间1秒 单位毫秒 long类型,一般数据不变时
	 *
	 * @return
	 */
	public static String readString(String parent,String file,Map<String,Object> map){
		try {
			var cache_key="file:"+parent+file;
			if(map.get("cache.file")!=null&&Jse.cache.containsKey(cache_key)) {
				return map.get(cache_key).toString();
			}
			Path p=null;
			if(parent.equals("classpath"))p=Path.of(Jse.classpath,file);
			else if(parent.equals("js")||parent.equals("jspath"))p=Path.of(Jse.jspath(),file);
			else if(parent.equals("webapp")||parent.equals("web"))p=Path.of(Jse.webapp(),file);
			else if(parent.equals("webapps"))p=Path.of(Jse.webapps(),file);
			else if(parent.equals("jse")||parent.equals("jar")) {//resource一般不会太大 太大用Io.read 一搬不大直接读
				return new String(Fs.class.getResourceAsStream(file).readAllBytes(),CHARSET);
			}else p=Path.of(parent,file);
			var s=new StringBuilder();
			if(map.containsKey("tpl")) {//包含模板
				Object tpl=map.get("tpl");
				if("tpl".equals(tpl)||"enjoy".equals(tpl)) {
					s.append(Tpl.use().getTemplateByString(Files.readString(p), false).renderToString(map));
				}else if("jst".equals(tpl)) {
					s.append(Jst.renderToString(Files.readString(p), map));
				}else if("js".equals(tpl)) {
					var x=Fs.read(parent,file).replace("`","@jse@");
					s.append(Js.fun("tpljs",x, map).toString().replace("@jse@","`"));
				}
			}else {
				Files.readAllLines(p,CHARSET).forEach(x->{
					int include_idx=x.indexOf("__jseinclude__");
					int var_idx=x.indexOf("__jse__");
					if(include_idx!=-1) {
						var f=x.substring(include_idx+14).replace(" ","");
						var s0=readString(parent,f,map);
						s.append(s0).append(LINE);
					}else if(var_idx!=-1) {
						var v0=x.substring(0,var_idx);
						var v=x.substring(var_idx+7);
						var v1="";
						int v_idx0=v.indexOf(';');
						int v_idx1=v.indexOf('|');
						if(v_idx0!=-1) {
							v1=v.substring(v_idx0);
							v=v.substring(0,v_idx0);
						}else if(v_idx1!=-1) {
							v1=v.substring(v_idx1+1);
							v=v.substring(0,v_idx1);
						}
						var o=map.get(v);
						s.append(v0).append(o).append(v1).append(LINE);
					}else s.append(x).append(LINE);
				});
			}
			if(map.get("cache.file")!=null) {
				Jse.cache.put(cache_key,s.toString(),(long)map.get("cache.file"));
			}
			return s.toString();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	public static boolean mkdirs(String p) {
		try {var path=Path.of(p);if(!Files.exists(path))Files.createDirectories(path);return true;
		}catch(IOException e){e.printStackTrace();return false;}
	}
	public static boolean copy(Path src, Path dest) {
		try {
			BasicFileAttributes srcAttr = Files.readAttributes(src, BasicFileAttributes.class);
			if (srcAttr.isDirectory()) {
				Files.walkFileTree(src, EnumSet.of(java.nio.file.FileVisitOption.FOLLOW_LINKS),Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
					@Override
					public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
						Files.createDirectories(dest.resolve(src.relativize(dir)));
						return FileVisitResult.CONTINUE;
					}
					@Override
					public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
						Files.copy(file, dest.resolve(src.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
						return FileVisitResult.CONTINUE;
					}
				});
			}
			else if (srcAttr.isRegularFile()) {
				Files.copy(src,dest);
			}
			else {
				throw new IllegalArgumentException("Source File must denote a directory or file");
			}
			return true;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String suffix(String s) {
    	if(s==null)return "";
    	var idx=s.indexOf('?');if(idx!=-1)s=s.substring(0,idx);
		var index=s.lastIndexOf('.');if(!s.endsWith("/")&&index!=-1)return s.substring(index+1,s.length());
		return "";
    }
	
	public static String mimeType(Object o) {
    	try {
    		if(o instanceof String s) {
        		return Files.probeContentType(Path.of(s.indexOf('.')==-1?"."+s:s));
        	}else if(o instanceof Path p) {
        		return Files.probeContentType(p);
        	}else if(o instanceof File f) {
        		return Files.probeContentType(f.toPath());
        	}
			return Files.probeContentType(Path.of(o.toString()));
		} catch (IOException e) {
			return null;//"application/octet-stream"
		}
    }
	
	public static String mimeType(Object o,String def) {
		return Optional.ofNullable(mimeType(o)).orElse(def);
	}
	
	public static Path jdk() {
		var javaHome=Path.of(Jse.home);
		var javac=File.separatorChar=='/'?"javac":"javac.exe";
	    return Stream.of(javaHome, javaHome.getParent())
	        .map(home -> home.resolve("bin").resolve(javac))
	        .filter(Files::exists)
	        .map(p -> p.getParent().getParent())
	        .findFirst().get();
	  }
	
	public static boolean exists(String url){return Files.exists(Path.of(url));}
	public static boolean exists(String parent, String file){
		Path p=null;
		if(parent.equals("classpath"))p=Path.of(Jse.classpath,file);
		else if(parent.equals("js")||parent.equals("jspath"))p=Path.of(Jse.jspath(),file);
		else if(parent.equals("webapp")||parent.equals("web"))p=Path.of(Jse.webapp(),file);
		else if(parent.equals("webapps"))p=Path.of(Jse.webapps(),file);
		else if(parent.equals("jse")||parent.equals("jar")) {//resource一般不会太大 太大用Io.read 一搬不大直接读
			return Fs.class.getResourceAsStream(file)!=null;
		}else p=Path.of(parent,file);
		return Files.exists(p);
	}
	public static String readString(Path p) {
		try{return Files.readString(p);}catch(IOException e){throw new RuntimeException(e);}
	}
	public static long lastModifiedTime(Path path) {
		try {return Files.getLastModifiedTime(path).toMillis();
		} catch (IOException e) {throw new RuntimeException(e);}
	}
	public static byte[] readAllBytes(Path f) {
		try {
			return Files.readAllBytes(f);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static boolean remove(String p) {return delete(Path.of(p));}
	public static boolean delete(String p) {return delete(Path.of(p));}
	public static boolean delete(Path path){
        try {
			return Files.deleteIfExists(path);
		} catch (DirectoryNotEmptyException e) {
	        try {
				Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
				    @Override
				    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
				        Files.delete(file);
				        return FileVisitResult.CONTINUE;
				    }
				    @Override
				    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
				        Files.delete(dir);
				        return super.postVisitDirectory(dir, exc);
				    }
				});
				return true;
			} catch (IOException e1) {
				e1.printStackTrace();
				return false;
			}
	    } catch(IOException e) {
	    	e.printStackTrace();
			return false;
	    }
    }
	public static Path createFile(String f) {
		try {
			var p=Path.of(f);
			if(!Files.exists(p)) {
				Files.createDirectories(p.getParent());
				Files.createFile(p);
			}
			return p;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	public static Path writeString(String f,String s) {
		try {
			var p=createFile(f);
			Files.writeString(p, s);
			return p;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	public static Path write(String f,byte[] s) {
		try {
			var p=createFile(f);
			Files.write(p, s);
			return p;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	public static void move(String src,String target,boolean replace) {
		try {
			Files.move(Path.of(src),Path.of(target),StandardCopyOption.REPLACE_EXISTING);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public static void main(String[] args) throws IOException {
//		var s=Js.execute("""
//				Fs.readString('D:/work/xjy/web', "index2.html",{tpl:"js",abc:88})
//				""");
		var s=Fs.readString("D:/work/xjy/web", "index2.html",Map.of("tpl","jst","abc",876,"session.user","{}"));
		System.out.println(s);
	}
	
}
